#include "drv_qspi_w25qxx.h"
#include "bsp_qspi.h"


/* QSPI-W25QXX 驱动 */

#define W25QXX_SIZE         (8*1024*1024)      //flash大小, 目前使用W25Q64JV

#define W25QXX_ID_64        0xEF16
#define W25QXX_ID_128       0xEF17

#define W25QXX_CMD_WRITE_DISABLE 0x04
#define W25QXX_CMD_WRITE_ENABLE 0x06
#define W25QXX_CMD_WRITE    0x32
#define W25QXX_CMD_READ_STATUS_REG  0x05
#define W25QXX_CMD_READ_ID  0x94
#define W25QXX_CMD_READ     0xEB
#define W25QXX_CMD_ERASE_SECTOR 0x20
#define W25QXX_CMD_ERASE_CHIP 0xC7
#define W25QXX_CMD_ENABLE_RESET 0x66
#define W25QXX_CMD_RESET_DEVICE 0x99

/* static var */
static QSPI_HandleTypeDef *qspi_handle = NULL;


/* static func */

//写禁止
void drv_qspi_w25qxx_write_disable(void)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 基本配置 */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 24位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
    cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* 不支持DDR */
    cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;   /* 每次传输都发指令 */

    /* 写失能命令 Write Disable  */
    cmd.Instruction = W25QXX_CMD_WRITE_DISABLE; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_NONE;       /* 无地址 */
    cmd.DataMode = QSPI_DATA_NONE;             /* 无数据 */
    cmd.Address = 0x000000;                    /* 地址 */
    cmd.DummyCycles = 0;                       /* 0空周期 */
    cmd.NbData = 0;                            /* 无数据数据 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }
}

//写使能
void drv_qspi_w25qxx_write_enable(void)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 基本配置 */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 24位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
    cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* 不支持DDR */
    cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;   /* 每次传输都发指令 */

    /* 写使能命令 Write Enable */
    cmd.Instruction = W25QXX_CMD_WRITE_ENABLE; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_NONE;       /* 无地址 */
    cmd.DataMode = QSPI_DATA_NONE;             /* 无数据 */
    cmd.Address = 0x000000;                    /* 地址 */
    cmd.DummyCycles = 0;                       /* 0空周期 */
    cmd.NbData = 0;                            /* 无数据数据 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }
}

//等待qspi就绪，用于flash擦除和页编程时使用
static void drv_qspi_w25qxx_auto_polling(void)
{
    QSPI_CommandTypeDef cmd = {0};
    QSPI_AutoPollingTypeDef config = {0};

    /* 基本配置 */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 24位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
    cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* 不支持DDR */
    cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;   /* 每次传输都发指令 */

    /* 读取状态 Read Status Register-1  */
    cmd.Instruction = W25QXX_CMD_READ_STATUS_REG; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_NONE;       /* 无地址 */
    cmd.DataMode = QSPI_DATA_1_LINE;             /* 1线数据 */
    cmd.Address = 0x000000;                    /* 地址 */
    cmd.DummyCycles = 0;                       /* 0空周期 */
    cmd.NbData = 0;                            /* 无数据数据 */

    /* 屏蔽位设置的bit0，匹配位等待bit0为0，即不断查询状态寄存器bit0，等待其为0 */
    config.Mask = 0x01;
    config.Match = 0x00;
    config.MatchMode = QSPI_MATCH_MODE_AND;
    config.StatusBytesSize = 1;
    config.Interval = 0x10;
    config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_QSPI_AutoPolling(qspi_handle, &cmd, &config, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }
}

void drv_qspi_w25qxx_soft_reset(void)
{
    // drv_qspi_w25qxx_auto_polling();

    QSPI_CommandTypeDef cmd = {0};

    /* 基本配置 */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 24位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
    cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* 不支持DDR */
    cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;   /* 每次传输都发指令 */

    /* 写使能命令 Write Enable */
    cmd.Instruction = W25QXX_CMD_ENABLE_RESET; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_NONE;       /* 无地址 */
    cmd.DataMode = QSPI_DATA_NONE;             /* 无数据 */
    cmd.Address = 0x000000;                    /* 地址 */
    cmd.DummyCycles = 0;                       /* 0空周期 */
    cmd.NbData = 0;                            /* 无数据数据 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }

    cmd.Instruction = W25QXX_CMD_RESET_DEVICE; /* 命令 */
    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }

}


/* export func */

/**
 * @brief 初始化
 * 
 */
int drv_qspi_w25qxx_init(void)
{
    uint32_t id;

    bsp_qspi_init(W25QXX_SIZE);
    qspi_handle = bsp_qspi_get_handle();

    drv_qspi_w25qxx_soft_reset();
    id = drv_qspi_w25qxx_read_id();
    if (id != W25QXX_ID_64)
    {
        printf("QSPI W25QXX ID = 0x%X\r\n", id);
        return -1;
    }
    return 0;
}

/**
 * @brief 读取ID
 * 
 * @return uint32_t 设备ID 
 */
uint32_t drv_qspi_w25qxx_read_id(void)
{
    QSPI_CommandTypeDef cmd = {0};
    uint8_t buf[2] = {0};
    uint32_t id;

    /* 基本配置 */
    cmd.InstructionMode   = QSPI_INSTRUCTION_1_LINE;    /* 1线方式发送指令 */
    cmd.AddressSize       = QSPI_ADDRESS_24_BITS;       /* 24位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;  /* 无交替字节 */
    cmd.DdrMode           = QSPI_DDR_MODE_DISABLE;      /* 不支持DDR */
    cmd.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;   /* 每次传输都发指令 */

    /* 读取 Manufacturer/Device ID Quad I/O */
    cmd.Instruction = W25QXX_CMD_READ_ID;   /* 读取ID命令 */
    cmd.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */
    cmd.DataMode = QSPI_DATA_4_LINES;       /* 4线数据 */
    cmd.Address = 0x000000;                 /* 地址 */
    cmd.DummyCycles = 6;                    /* 6空周期 */
    cmd.NbData = 2;                         /* 读取三个数据 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi cmd fail\r\n");
    }

    if (HAL_QSPI_Receive(qspi_handle, buf, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
        printf("qspi recv fail\r\n");
    }

    id = ((buf[0] << 8) | (buf[1] << 0));

    return id;
}

/**
 * @brief 读数据
 * 
 * @param addr 地址
 * @param pbuf 数据缓存
 * @param size 数据大小
 */
void drv_qspi_w25qxx_read(uint32_t addr, uint8_t *pbuf, uint32_t size)
{
    QSPI_CommandTypeDef cmd = {0};

    /* 基本配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;            /* 32位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;               /* W25Q256JV不支持DDR */
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;           /* 每次传输要发指令 */

    /* 读取数据 Fast Read Quad I/O */
    cmd.Instruction = W25QXX_CMD_READ;      /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */
    cmd.DataMode = QSPI_DATA_4_LINES;       /* 4线数据 */
    cmd.Address = addr;                     /* 地址 */
    cmd.DummyCycles = 6;                    /* 6空周期 */
    cmd.NbData = size;                      /* 读取的数据大小 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

    /* 读取 */
    if (HAL_QSPI_Receive(qspi_handle, pbuf, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }
}

/**
 * @brief 写数据
 * 
 * @param addr 地址
 * @param pbuf 数据缓存
 * @param size 数据大小
 */
void drv_qspi_w25qxx_write(uint32_t addr, uint8_t *pbuf, uint32_t size)
{
    QSPI_CommandTypeDef cmd = {0};

    drv_qspi_w25qxx_write_enable();     //写使能

    /* 基本配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;            /* 32位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;               /* W25Q256JV不支持DDR */
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;           /* 每次传输要发指令 */

    /* 写数据 Quad Input Page Program */
    cmd.Instruction = W25QXX_CMD_WRITE;     /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_1_LINE;  /* 1线地址 */
    cmd.DataMode = QSPI_DATA_4_LINES;       /* 4线数据 */
    cmd.Address = addr;                     /* 地址 */
    cmd.DummyCycles = 0;                    /* 0空周期 */
    cmd.NbData = size;                      /* 读取的数据大小 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

    /* 读取 */
    if (HAL_QSPI_Transmit(qspi_handle, pbuf, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

    drv_qspi_w25qxx_auto_polling();
}

/**
 * @brief 擦除指定扇区，扇区大小4KB
 * 
 * @param sector 擦除地址，以4KB为单位的地址，比如0，4096, 8192等
 */
void drv_qspi_w25qxx_erase_sector(uint32_t sector)
{
    QSPI_CommandTypeDef cmd = {0};
 
    // sector /= 4096;
    // sector *= 4096;
    // sector &= 0xFFFFF000;   //保证以4K的整数倍
    drv_qspi_w25qxx_write_enable();

    /* 基本配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;            /* 32位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;               /* W25Q256JV不支持DDR */
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;           /* 每次传输要发指令 */

    /* 擦除扇区 Sector Erase */
    cmd.Instruction = W25QXX_CMD_ERASE_SECTOR; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_1_LINE;     /* 1线地址 */
    cmd.DataMode = QSPI_DATA_NONE;             /* 无数据 */
    cmd.Address = sector;                      /* 地址，保证是4KB整数倍 */
    cmd.DummyCycles = 0;                       /* 0空周期 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

    drv_qspi_w25qxx_auto_polling();
}

/**
 * @brief 整片擦除
 * 
 */
void drv_qspi_w25qxx_erase_chip(void)
{
    QSPI_CommandTypeDef cmd = {0};

    drv_qspi_w25qxx_write_enable();

    /* 基本配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;            /* 32位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;               /* W25Q256JV不支持DDR */
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;           /* 每次传输要发指令 */

    /* 擦除扇区 Chip Erase */
    cmd.Instruction = W25QXX_CMD_ERASE_CHIP; /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_NONE;     /* 无地址 */
    cmd.DataMode = QSPI_DATA_NONE;           /* 无数据 */
    cmd.Address = 0;                         /* 地址 */
    cmd.DummyCycles = 0;                     /* 0空周期 */

    if (HAL_QSPI_Command(qspi_handle, &cmd, 10000) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

    drv_qspi_w25qxx_auto_polling();
}

/**
 * @brief QSPI内存映射，地址 0x90000000
 * 
 */
void drv_qspi_w25qxx_memory_mapped(void)
{
    QSPI_CommandTypeDef cmd = {0};
    QSPI_MemoryMappedTypeDef cfg = {0};

    /* 基本配置 */
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;     /* 1线方式发送指令 */
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;            /* 32位地址 */
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字节 */
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;               /* W25Q256JV不支持DDR */
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;  /* DDR模式，数据输出延迟 */
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;           /* 每次传输要发指令 */

    /* 读取数据 Fast Read Quad I/O */
    cmd.Instruction = W25QXX_CMD_READ;      /* 命令 */
    cmd.AddressMode = QSPI_ADDRESS_4_LINES; /* 4线地址 */
    cmd.DataMode = QSPI_DATA_4_LINES;       /* 4线数据 */
    cmd.DummyCycles = 6;                    /* 6空周期 */

    /* 关闭溢出计数 */
    cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
    cfg.TimeOutPeriod = 0;

    if (HAL_QSPI_MemoryMapped(qspi_handle, &cmd, &cfg) != HAL_OK)
    {
        // Error_Handler(__FILE__, __LINE__);
    }

}


uint32_t drv_qspi_w25qxx_get_size(void)
{
    return W25QXX_SIZE;
}
